package com.hzgj.bcl.soa.collector;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;

public class JmxCollector {
    private static final Logger logger = LoggerFactory.getLogger(JmxCollector.class.getName());

    private static final Pattern snakeCasePattern = Pattern.compile("([a-z0-9])([A-Z])");
    private static final Pattern PROPERTY_PATTERN = Pattern.compile(
            "([^,=:\\*\\?]+)" + // Name - non-empty, anything but comma, equals, colon, star, or question mark
                    "=" +  // Equals
                    "(" + // Either
                    "\"" + // Quoted
                    "(?:" + // A possibly empty sequence of
                    "[^\\\\\"]" + // Anything but backslash or quote
                    "|\\\\\\\\" + // or an escaped backslash
                    "|\\\\n" + // or an escaped newline
                    "|\\\\\"" + // or an escaped quote
                    "|\\\\\\?" + // or an escaped question mark
                    "|\\\\\\*" + // or an escaped star
                    ")*" +
                    "\"" +
                    "|" + // Or
                    "[^,=:\"]*" + // Unquoted - can be empty, anything but comma, equals, colon, or quote
                    ")");

    private String jmxUrl;
    private String username;
    private String password;
    MBeanServerConnection connection;
    JMXConnector jmxc = null;


    public JmxCollector(Map<String, Object> config) throws MalformedObjectNameException {
        if (config == null) {
            config = new HashMap<>();
        }

        if (config.containsKey("hostPort")) {
            if (config.containsKey("jmxUrl")) {
                throw new IllegalArgumentException("At most one of hostPort and jmxUrl must be provided");
            }
            jmxUrl = "service:jmx:rmi:///jndi/rmi://" + config.get("hostPort") + "/jmxrmi";
        } else if (config.containsKey("jmxUrl")) {
            jmxUrl = (String) config.get("jmxUrl");
        } else {
            // Default to local JVM
            jmxUrl = "";
        }

        if (config.containsKey("username")) {
            username = (String) config.get("username");
        } else {
            // Any username.
            username = "";
        }

        if (config.containsKey("password")) {
            password = (String) config.get("password");
        } else {
            // Empty password.
            password = "";
        }
    }

    public void start() throws Exception {
        if (jmxUrl.isEmpty()) {
            connection = ManagementFactory.getPlatformMBeanServer();
        } else {
            HashMap credential = null;
            if (username != null && username.length() != 0 && password != null && password.length() != 0) {
                credential = new HashMap();
                String[] credent = new String[]{username, password};
                credential.put(JMXConnector.CREDENTIALS, credent);
            }
            final ExecutorService executor = Executors.newSingleThreadExecutor();
            HashMap _credential = credential;
            Future<JMXConnector> future = executor.submit(() -> {
                try {
                    return JMXConnectorFactory.connect(new JMXServiceURL(jmxUrl), _credential);
                } catch (IOException e) {
                    logger.error("{}", e);
                }
                return null;
            });
            jmxc = future.get(2, TimeUnit.SECONDS);
            if (jmxc != null) {
                connection = jmxc.getMBeanServerConnection();
            } else {
                throw new Exception("can't connect to " + jmxUrl);
            }
        }
    }

    public Object invoke(ObjectName objectName, String actionName, Object[] params, String[] signature) throws Exception {
        return connection.invoke(objectName, actionName, params, signature);
    }

    public Object invokeWithoutParam(String objectName, String actionName) throws Exception {
        return invoke(new ObjectName(objectName), actionName, new Object[]{}, new String[]{});
    }

    public Object invoke(String objectName, String actionName, Object[] params, String[] signature) throws Exception {
        return invoke(new ObjectName(objectName), actionName, params, signature);
    }
}
